Next: Useful Techniques, Previous: Expected Failures, Up: How to Write Tests [Contents]
Sometimes, it doesn’t make sense to run a test due to
missing preconditions. A required Emacs feature might not be
compiled in, the function to be tested could call an external
binary which might not be available on the test machine, you name
it. In this case, the macro skip-unless could be
used to skip the test:
(ert-deftest test-dbus () "A test that checks D-BUS functionality." (skip-unless (featurep 'dbusbind)) ...)
The outcome of running a test should not depend on the current state of the environment, and each test should leave its environment in the same state it found it in. In particular, a test should not depend on any Emacs customization variables or hooks, and if it has to make any changes to Emacs’s state or state external to Emacs (such as the file system), it should undo these changes before it returns, regardless of whether it passed or failed.
Tests should not depend on the environment because any such
dependencies can make the test brittle or lead to failures that
occur only under certain circumstances and are hard to reproduce.
Of course, the code under test may have settings that affect its
behavior. In that case, it is best to make the test
let-bind all such setting variables to set up a
specific configuration for the duration of the test. The test can
also set up a number of different configurations and run the code
under test with each.
Tests that have side effects on their environment should
restore it to its original state because any side effects that
persist after the test can disrupt the workflow of the programmer
running the tests. If the code under test has side effects on
Emacs’s current state, such as on the current buffer or
window configuration, the test should create a temporary buffer
for the code to manipulate (using with-temp-buffer),
or save and restore the window configuration (using
save-window-excursion), respectively. For aspects of
the state that can not be preserved with such macros, cleanup
should be performed with unwind-protect, to ensure
that the cleanup occurs even if the test fails.
An exception to this are messages that the code under test
prints with message and similar logging; tests
should not bother restoring the *Message* buffer to
its original state.
The above guidelines imply that tests should avoid calling
highly customizable commands such as find-file,
except, of course, if such commands are what they want to test.
The exact behavior of find-file depends on many
settings such as find-file-wildcards,
enable-local-variables, and
auto-mode-alist. It is difficult to write a
meaningful test if its behavior can be affected by so many
external factors. Also, find-file has side effects
that are hard to predict and thus hard to undo: It may create a
new buffer or reuse an existing buffer if one is already visiting
the requested file; and it runs find-file-hook,
which can have arbitrary side effects.
Instead, it is better to use lower-level mechanisms with
simple and predictable semantics like
with-temp-buffer, insert or
insert-file-contents-literally, and to activate any
desired mode by calling the corresponding function directly,
after binding the hook variables to nil. This avoids
the above problems.
Next: Useful Techniques, Previous: Expected Failures, Up: How to Write Tests [Contents]